package com.song.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.song.api.BaseService;
import com.song.enums.Sex;
import com.song.pojo.AppUser;
import com.song.pojo.Fans;
import com.song.pojo.eo.FansEO;
import com.song.pojo.vo.FansCountsVO;
import com.song.pojo.vo.RegionRatioVO;
import com.song.user.mapper.FansMapper;
import com.song.user.service.MyFansService;
import com.song.user.service.UserService;
import com.song.utils.PagedGridResult;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.n3r.idworker.Sid;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class MyFansServiceImpl extends BaseService implements MyFansService {


    @Autowired
    private FansMapper fansMapper;

    @Autowired
    private UserService userService;

    @Autowired
    public Sid sid;

    @Autowired
    private ElasticsearchTemplate esTemplate;

    /**
     * 被动更新粉丝用户信息
     *
     * @param relationId
     * @param fanId
     * @return
     */
    @Override
    public void forceUpdateFanInfo(String relationId, String fanId) {

        // 1、根据fanId查询用户最新信息
        AppUser user = userService.getUser(fanId);

        // 2、更新用户信息到db和es中
        Fans fans = new Fans();
        fans.setId(relationId);

        fans.setFace(user.getFace());
        fans.setSex(user.getSex());
        fans.setFanNickname(user.getNickname());
        fans.setProvince(user.getProvince());
        fansMapper.updateByPrimaryKeySelective(fans);

        Map<String, Object> map = new HashMap<>();
        map.put("face", user.getFace());
        map.put("sex", user.getSex());
        map.put("fanNickname", user.getNickname());
        map.put("province", user.getProvince());

        IndexRequest ir = new IndexRequest();
        ir.source(map);

        UpdateQuery uq = new UpdateQueryBuilder()
                .withClass(FansEO.class)
                .withId(relationId)
                .withIndexRequest(ir)
                .build();

        esTemplate.update(uq);


    }

    /**
     * 查询当前用户是否关注作家
     *
     * @param writerId
     * @param fanId
     * @return
     */
    @Override
    public Boolean isMeFollowThisWriter(String writerId, String fanId) {

        Fans fans = new Fans();
        fans.setFanId(fanId);
        fans.setWriterId(writerId);

        int count = fansMapper.selectCount(fans);

        return count == 1 ? true : false;
    }

    /**
     * 关注作家，成为粉丝
     *
     * @param writerId
     * @param fanId
     * @return
     */
    @Transactional
    @Override
    public void follow(String writerId, String fanId) {

        // 获取粉丝信息
        AppUser fansInfo = userService.getUser(fanId);

        String id = sid.nextShort();

        // 保存作家粉丝关联关系，字段冗余便于统计分析，并且只认成为第一次成为粉丝的数据
        Fans fans = new Fans();
        fans.setId(id);
        fans.setFanId(fanId);
        fans.setWriterId(writerId);
        fans.setFace(fansInfo.getFace());
        fans.setSex(fansInfo.getSex());
        fans.setFanNickname(fansInfo.getNickname());
        fans.setProvince(fansInfo.getProvince());

        fansMapper.insert(fans);

        // redis 作家粉丝累加
        redis.increment(REDIS_WRITER_FANS_COUNTS + writerId, 1);

        // redis 粉丝关注累加
        redis.increment(REDIS_MY_FOLLOW_COUNTS + fanId, 1);

        // 粉丝信息存入es
        FansEO fansEO = new FansEO();
        BeanUtils.copyProperties(fans, fansEO);
        IndexQuery iq = new IndexQueryBuilder().withObject(fansEO).build();
        esTemplate.index(iq);
    }

    /**
     * 取消关注作家
     *
     * @param writerId
     * @param fanId
     * @return
     */
    @Transactional
    @Override
    public void unfollow(String writerId, String fanId) {

        Fans fans = new Fans();
        fans.setFanId(fanId);
        fans.setWriterId(writerId);

        fansMapper.delete(fans);

        // redis 作家粉丝累减
        redis.decrement(REDIS_WRITER_FANS_COUNTS + writerId, 1);

        // redis 粉丝关注累减
        redis.decrement(REDIS_MY_FOLLOW_COUNTS + fanId, 1);

        // 删除es中的粉丝关系，DeleteQuery：根据条件删除
        DeleteQuery dq = new DeleteQuery();
        dq.setQuery(QueryBuilders.termQuery("writerId", writerId));
        dq.setQuery(QueryBuilders.termQuery("fanId", fanId));
        esTemplate.delete(dq, FansEO.class);

    }

    /**
     * 查询所有粉丝列表
     *
     * @param writerId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PagedGridResult queryAllFansList(String writerId, Integer page, Integer pageSize) {

        Fans fans = new Fans();
        fans.setWriterId(writerId);

        PageHelper.startPage(page, pageSize);
        List<Fans> list = fansMapper.select(fans);

        return setPagedGridResult(list, page);
    }

    /**
     * es中查询所有粉丝列表
     *
     * @param writerId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PagedGridResult queryAllFansESList(String writerId, Integer page, Integer pageSize) {

        page--;
        Pageable pageable = PageRequest.of(page, pageSize);

        SearchQuery sq = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery("writerId", writerId))
                .withPageable(pageable)
                .build();

        AggregatedPage<FansEO> pagedFans = esTemplate.queryForPage(sq, FansEO.class);

        PagedGridResult gridResult = new PagedGridResult();
        gridResult.setRows(pagedFans.getContent());
        gridResult.setPage(page + 1);
        gridResult.setTotal(pagedFans.getTotalPages());
        gridResult.setRecords(pagedFans.getTotalElements());

        return gridResult;
    }

    /**
     * 查询男女粉丝数量
     *
     * @param writerId
     * @param sex
     */
    @Override
    public Integer queryFansCounts(String writerId, Sex sex) {

        Fans fans = new Fans();
        fans.setWriterId(writerId);
        fans.setSex(sex.type);

        Integer count = fansMapper.selectCount(fans);
        return count;
    }

    @Override
    public FansCountsVO queryFansESCounts(String writerId) {

        TermsAggregationBuilder aggregationBuilder = AggregationBuilders
                .terms("sex_counts")
                .field("sex");

        SearchQuery sq = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery("writerId", writerId))
                .addAggregation(aggregationBuilder)
                .build();

        Aggregations aggregations = esTemplate.query(sq, new ResultsExtractor<Aggregations>() {
            @Override
            public Aggregations extract(SearchResponse searchResponse) {
                return searchResponse.getAggregations();
            }
        });

        Map asMap = aggregations.asMap();
        LongTerms longTerms = (LongTerms) asMap.get("sex_counts");
        List bucketList = longTerms.getBuckets();

        FansCountsVO fansCountsVO = new FansCountsVO();
        for (int i = 0; i < bucketList.size(); i++) {
            LongTerms.Bucket bucket = (LongTerms.Bucket) bucketList.get(i);
            Long key = (Long) bucket.getKey();
            Long docCount = bucket.getDocCount();
            if (key.intValue() == Sex.woman.type) {
                fansCountsVO.setWomanCounts(docCount.intValue());
            } else if (key.intValue() == Sex.man.type) {
                fansCountsVO.setManCounts(docCount.intValue());
            }
        }

        if (bucketList.size() == 0 || bucketList == null) {
            fansCountsVO.setManCounts(0);
            fansCountsVO.setWomanCounts(0);
        }

        return fansCountsVO;
    }

    public static final String[] regions = {"北京", "天津", "上海", "重庆",
            "河北", "山西", "辽宁", "吉林", "黑龙江", "江苏", "浙江", "安徽", "福建", "江西", "山东",
            "河南", "湖北", "湖南", "广东", "海南", "四川", "贵州", "云南", "陕西", "甘肃", "青海", "台湾",
            "内蒙古", "广西", "西藏", "宁夏", "新疆", "香港", "澳门"};

    /**
     * 查询每个地域的粉丝数量
     *
     * @param writerId
     */
    @Override
    public List<RegionRatioVO> queryRatioByRegionCounts(String writerId) {

        Fans fans = new Fans();
        fans.setWriterId(writerId);

        List<RegionRatioVO> list = new ArrayList<>();
        for (String r : regions) {
            fans.setProvince(r);
            int count = fansMapper.selectCount(fans);

            RegionRatioVO regionRatioVO = new RegionRatioVO();
            regionRatioVO.setName(r);
            regionRatioVO.setValue(count);

            list.add(regionRatioVO);
        }

        return list;
    }

    @Override
    public List<RegionRatioVO> queryRatioByRegionESCounts(String writerId) {
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders
                .terms("region_counts")
                .field("province");

        SearchQuery sq = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery("writerId", writerId))
                .addAggregation(aggregationBuilder)
                .build();

        Aggregations aggregations = esTemplate.query(sq, new ResultsExtractor<Aggregations>() {
            @Override
            public Aggregations extract(SearchResponse searchResponse) {
                return searchResponse.getAggregations();
            }
        });

        Map asMap = aggregations.asMap();
        StringTerms stringTerms = (StringTerms) asMap.get("region_counts");
        List bucketList = stringTerms.getBuckets();

        List<RegionRatioVO> list = new ArrayList<>();
        for (int i = 0; i < bucketList.size(); i++) {
            RegionRatioVO regionRatioVO = new RegionRatioVO();
            StringTerms.Bucket bucket = (StringTerms.Bucket) bucketList.get(i);
            String key = (String) bucket.getKey();
            Long docCount = bucket.getDocCount();
           regionRatioVO.setName(key);
           regionRatioVO.setValue(docCount.intValue());
           list.add(regionRatioVO);
        }

        return list;
    }
}
